﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace NetMicro.ExcelMapper.Attributes
{
    /// <summary>
    /// 列名特性
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public sealed class ColumnAttribute : Attribute
    {
        #region Properties

        /// <summary>
        /// 列索引
        /// </summary>
        public int Index { get; internal set; } = -1;

        /// <summary>
        /// 列名
        /// </summary>
        public string Name { get; internal set; }

        /// <summary>
        /// 属性名称
        /// </summary>
        public string PropertyName { get; set; }

        private PropertyInfo _property;
        /// <summary>
        /// 映射属性
        /// </summary>
        public PropertyInfo Property
        {
            get => _property;

            internal set
            {
                _property = value;

                if (value != null)
                {
                    PropertyUnderlyingType = Nullable.GetUnderlyingType(value.PropertyType);
                    PropertyUnderlyingConverter = PropertyUnderlyingType != null ? TypeDescriptor.GetConverter(PropertyUnderlyingType) : null;
                }
                else
                {
                    PropertyUnderlyingType = null;
                    PropertyUnderlyingConverter = null;
                }
            }
        }

        /// <summary>
        /// 如果属性是可为null的值类型，则获取基础类型，否则返回null。
        /// </summary>
        public Type PropertyUnderlyingType { get; private set; }

        /// <summary>
        /// 如果属性为可空值类型，则获取转换器
        /// </summary>
        public TypeConverter PropertyUnderlyingConverter { get; private set; }

        /// <summary>
        /// 指示是否使用最后一个非空值。
        /// 通常处理合并单元格中的空白错误。
        /// </summary>
        internal bool? UseLastNonBlankValue { get; set; }

        /// <summary>
        /// 是否忽略该属性
        /// </summary>
        internal bool? Ignored { get; set; }

        /// <summary>
        /// 获取或设置自定义格式
        /// </summary>
        public string CustomFormat { get; set; }

        /// <summary>
        /// 是否忽略列的所有错误
        /// </summary>
        public bool? IgnoreErrors { get; set; }

        /// <summary>
        /// 从文件导入数据时，尝试获取给定列的单元格值
        /// </summary>
        internal Func<IColumnInfo, object, bool> TryTake { get; set; }

        /// <summary>
        /// 将对象导出到文件时，尝试将给定列的值设置为单元格
        /// </summary>
        internal Func<IColumnInfo, object, bool> TryPut { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// 初始化
        /// </summary>
        public ColumnAttribute()
        {
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="index">列索引</param>
        public ColumnAttribute(ushort index)
        {
            Index = index;
        }
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="name">列名</param>
        public ColumnAttribute(string name)
        {
            Name = name;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// 克隆对象
        /// </summary>
        public ColumnAttribute Clone()
        {
            return (ColumnAttribute)MemberwiseClone();
        }

        /// <summary>
        /// 指定索引克隆对象
        /// </summary>
        /// <param name="index">列索引</param>
        public ColumnAttribute Clone(int index)
        {
            ColumnAttribute clone = Clone();
            clone.Index = index;
            return clone;
        }

        /// <summary>
        /// 从源对象合并属性。
        /// 将从源的指定属性更新所有属性。
        /// </summary>
        /// <param name="source">要从中合并的对象</param>
        /// <param name="overwrite">
        /// 如果指定了源的属性，是否覆盖源中的指定属性。
        /// 请注意，索引和名称一起被视为一个键属性
        /// </param>
        public void MergeFrom(ColumnAttribute source, bool overwrite = true)
        {
            if (source == null) return;

            if (source.Index >= 0 || source.Name != null)
            {
                if (overwrite || (Index < 0 && Name == null))
                {
                    Index = source.Index;
                    Name = source.Name;
                }
            }

            if (source.Property != null && (overwrite || Property == null)) Property = source.Property;
            if (source.UseLastNonBlankValue != null && (overwrite || UseLastNonBlankValue == null)) UseLastNonBlankValue = source.UseLastNonBlankValue;
            if (source.Ignored != null && (overwrite || Ignored == null)) Ignored = source.Ignored;
            if (source.CustomFormat != null && (overwrite || CustomFormat == null)) CustomFormat = source.CustomFormat;
            if (source.IgnoreErrors != null && (overwrite || IgnoreErrors == null)) IgnoreErrors = source.IgnoreErrors;

            if (overwrite || TryPut == null) TryPut = source.TryPut;
            if (overwrite || TryTake == null) TryTake = source.TryTake;
        }

        /// <summary>
        /// 将属性合并到属性字典
        /// </summary>
        /// <param name="attributes">要合并到的字典</param>
        /// <param name="overwrite">
        /// 如果指定了现有对象的属性，是否将指定的属性覆盖到该对象。
        /// 请注意，索引和名称一起被视为一个键属性。
        /// </param>
        public void MergeTo(Dictionary<PropertyInfo, ColumnAttribute> attributes, bool overwrite = true)
        {
            if (attributes == null) return;
            PropertyInfo pi = Property;
            if (pi == null) return;

            ColumnAttribute existed = attributes.ContainsKey(pi) ? attributes[pi] : null;
            bool isIndexSet = Index >= 0;

            if (isIndexSet && !overwrite)
                if (attributes.Any(p => p.Key != pi && p.Value.Index == Index))
                {
                    // 如果已经设置了相同的索引，则清除索引 (with overwrite = false).
                    Index = -1;
                    isIndexSet = false;
                }

            if (existed != null)
            {
                isIndexSet = isIndexSet && ((existed.Index != Index) || overwrite);
                existed.MergeFrom(this, overwrite);
                isIndexSet = isIndexSet && (existed.Index == Index);
            }
            else
            {
                attributes[pi] = this;
            }

            if (isIndexSet) // 如果索引设置成功，则为True，否则它将被忽略/清除。
            {
                // 如果其他属性的索引相同，则清除它们的索引
                attributes.Where(p => p.Key != pi && p.Value.Index == Index).ToList().ForEach(p => p.Value.Index = -1);
            }
        }



        #endregion
    }
}
